package cn.fatgod.learn.thymeleaf.ast;

import cn.fatgod.learn.thymeleaf.format.CodeFormat;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.EnumDeclaration;
import com.github.javaparser.ast.body.TypeDeclaration;

import java.util.Objects;

public class AstUtils {
    /**
     * 解析源码
     *
     * @param source 源码
     * @return 类型声明
     */
    public static TypeDeclaration<?> parse(final String source, final String typeName) {
        return new JavaParser().parse(source).getResult()
                .orElseThrow(() ->
                        new RuntimeException(StrUtil.format("解析抽象语法树AST失败，源码为{}", source)))
                .getTypes()
                .stream()
                .filter(td -> td.getNameAsString().equals(typeName))
                .findAny()
                .orElseThrow(() ->
                        new RuntimeException(StrUtil.format("抽象语法树AST中不包含类型{}，源码为{}", typeName, source)));
    }

    /**
     * 在根类型声明中搜索指定类型
     * @param td 根类型声明
     * @param typeName 类型名称
     * @return 指定类型的声明
     */
    public static TypeDeclaration<?> searchType(TypeDeclaration<?> td, String typeName) {
        if (StrUtil.isBlank(typeName)) {
            throw new RuntimeException("类型搜索时目标类型名称不能为空");
        }
        if (td == null) {
            throw new RuntimeException("类型搜索时根类型声明不能为空");
        }
        String[] sp = typeName.split("\\.");
        if (ArrayUtil.isEmpty(sp)) {
            throw new RuntimeException(StrUtil.format("类型名称{}非法，不能为空", typeName));
        }
        Node root = td;
        for (int i = 1; i < sp.length; i++) {
            final String tn = sp[i];
            root = root.getChildNodes().stream()
                    .filter(n -> isType(n) && Objects.equals(((TypeDeclaration<?>) n).getNameAsString(), tn))
                    .findAny()
                    .orElseThrow(() ->
                            new RuntimeException(StrUtil.format("类型声明{}中找不到类型{}", typeName, tn)));
        }
        return (TypeDeclaration<?>) root;
    }

    /**
     * 判断node是否为类型声明
     *
     * @param node 声明节点
     * @return 是否为类型声明
     */
    public static boolean isType(Node node) {
        return node instanceof TypeDeclaration;
    }

    /**
     * 判断node是否为枚举声明
     *
     * @param node 声明节点
     * @return 是否为枚举声明
     */
    public static boolean isEnum(Node node) {
        return node instanceof EnumDeclaration;
    }

    /**
     * 美化编译单元生成的代码
     *
     * @param unit 编译单元
     * @return 美化后代码
     */
    public static String beautifyCode(CompilationUnit unit) {
        return CodeFormat.pretty(unit.toString());
    }
}
